`include "mycpu.h"

module cp0_regs(
		input		clk,
		input		reset,
		input		[`WS_TO_CR_BUS_WD -1:0] ws_to_cr_bus,
    input   found,
    input op_tlbp,
    input op_tlbwi,
    input op_tlbr,
    input [7:0]cw_asid,
    input [18:0]cw_vpn2,
    input [19:0]cw_pfn0,
    input [2:0]cw_c0,
    input cw_d0,
    input cw_v0,
    input cw_g0,
    input [19:0]cw_pfn1,
    input [2:0]cw_c1,
    input cw_d1,
    input cw_v1,
    input cw_g1,
    input [3:0]cw_index,
		output reg [31:0]c0_rdata,
    output reg [7:0]c0_asid,
    output reg [18:0]c0_vpn2,
    output reg [19:0]c0_pfn0,
    output reg [2:0]c0_c0,
    output reg c0_d0,
    output reg c0_v0,
    output reg c0_g0,
    output reg [19:0]c0_pfn1,
    output reg [2:0]c0_c1,
    output reg c0_d1,
    output reg c0_v1,
    output reg c0_g1,
    output reg [3:0]c0_index,
		output 	interrupt
);

wire		[4:0]c0_addr;
wire		[31:0]c0_wdata;
wire		wb_valid;
wire		wb_ex;
wire		wb_bd;
wire		[4:0]wb_excode;
wire		[31:0]wb_pc;
wire		op_mtc0;
wire		eret_flush;
wire		[5:0]ext_int_in;
wire        [31:0] wb_badvaddr;	
wire        count_eq_compare;

assign		{			wb_badvaddr,
						c0_addr,			//84:80
						c0_wdata,		//79:48
						wb_valid,
						wb_ex,
						wb_bd,			//47:45
						wb_excode, //44:40
						wb_pc,			//39:8
						op_mtc0,
						eret_flush,//7:6
						ext_int_in //5:0
											}=ws_to_cr_bus;



/* status bev */
wire		c0_status_bev;
assign	c0_status_bev=1'b1;

/* status im1~im7 */
wire		mct0_we;
assign	mct0_we = wb_valid && op_mtc0 && !wb_ex;
reg 		[7:0] c0_status_im;
always	@(posedge clk) begin
	if(mct0_we && c0_addr==`CR_STATUS)
		c0_status_im <= c0_wdata[15:8];
end

/* status exl */
reg 		c0_status_exl;
always	@(posedge clk) begin
	if(reset)
		c0_status_exl <= 1'b0;
	else if(wb_ex)
		c0_status_exl <= 1'b1;
	else if(eret_flush)
		c0_status_exl <= 1'b0;
	else if(mct0_we && c0_addr==`CR_STATUS)
		c0_status_exl <= c0_wdata[1];
end

/* status ie */
reg 		c0_status_ie;
always	@(posedge clk) begin
	if(reset)
		c0_status_ie <= 1'b0;
	else if(mct0_we && c0_addr==`CR_STATUS)
		c0_status_ie <= c0_wdata[0];
end

/* cause bd */
reg 		c0_cause_bd;
always	@(posedge clk) begin
	if(reset)
		c0_cause_bd <= 1'b0;
	else if(wb_ex && !c0_status_exl)
		c0_cause_bd <= wb_bd;
end

/* cause ti */
reg 		c0_cause_ti;
always	@(posedge clk) begin
	if(reset)
		c0_cause_ti <= 1'b0;
	else if(mct0_we && c0_addr==`CR_COMPARE)
		c0_cause_ti <= 1'b0;
	else if(count_eq_compare)
		c0_cause_ti <= 1'b1;
end

/* cause ip7~ip2 */
reg 		[7:0] c0_cause_ip;
always	@(posedge clk) begin
	if(reset)
		c0_cause_ip[7:2] <= 6'b0;
	else begin
		c0_cause_ip[7] <= ext_int_in[5] | c0_cause_ti;
		c0_cause_ip[6:2] <= ext_int_in[4:0];
	end
end
/* cause ip1~ip0 */
always	@(posedge clk) begin
	if(reset)
		c0_cause_ip[1:0] <= 2'b0;
	else if(mct0_we && c0_addr==`CR_CAUSE)
		c0_cause_ip[1:0] <= c0_wdata[9:8];
end

/* cause excode */
reg 		[4:0] c0_cause_excode;
always	@(posedge clk) begin
	if(reset)
		c0_cause_excode <= 5'b0;
	else if(wb_ex && !c0_status_exl)
		c0_cause_excode <= wb_excode;
end

/* epc */
reg [31:0] c0_epc;
always	@(posedge clk) begin
	if(wb_ex && !c0_status_exl)
		c0_epc <= wb_bd? wb_pc -3'h4 : wb_pc ;
	else if(mct0_we && c0_addr==`CR_EPC)
		c0_epc <= c0_wdata;
end


/* count */
reg	tick;
reg [31:0] c0_count;
always @(posedge clk) begin
	if(reset) tick <= 1'b0;
	else tick <= ~tick;
	if(mct0_we && c0_addr == `CR_COUNT)
		c0_count <= c0_wdata;
	else if(tick) c0_count <= c0_count + 1'b1; 
end

/* compare */
reg [31:0] c0_compare;

always @(posedge clk) begin
	if(reset) c0_compare <= 32'h0;
	else if(mct0_we && c0_addr == `CR_COMPARE)
			c0_compare <= c0_wdata;
end

assign count_eq_compare = (c0_compare != 0) & (c0_count != 0) 
							& (c0_compare == c0_count);

/* badvaddr */
reg [31:0] c0_badvaddr;
always @(posedge clk) begin
	if (wb_valid && wb_ex && (wb_excode == 5'h01 || wb_excode == 5'h02||wb_excode == 5'h03 || wb_excode == 5'h04|| wb_excode == 5'h05))
 		c0_badvaddr <= wb_badvaddr;
end

/*entryhi*/
always @(posedge clk) begin
  if(op_tlbr)begin
    c0_asid<=cw_asid;
		c0_vpn2<=cw_vpn2;
  end
	else if(mct0_we && c0_addr==`CR_ENTRIHI)begin
		c0_asid<=c0_wdata[7:0];
		c0_vpn2<=c0_wdata[31:13];
	end
	else if(wb_ex&&(wb_excode == 5'h01 || wb_excode == 5'h02||wb_excode == 5'h03))begin
		c0_vpn2 <= wb_badvaddr[31:13];
	end
end
/*entrylo0*/
always @(posedge clk) begin
  if(op_tlbr)begin
    c0_pfn0<=cw_pfn0;
		c0_c0<=cw_c0;
		c0_d0<=cw_d0;
		c0_v0<=cw_v0;
		c0_g0<=cw_g0;
  end
	else if(mct0_we && c0_addr==`CR_ENTRILO0)begin
		c0_pfn0<=c0_wdata[25:6];
		c0_c0<=c0_wdata[5:3];
		c0_d0<=c0_wdata[2];
		c0_v0<=c0_wdata[1];
		c0_g0<=c0_wdata[0];
	end
end
/*entrylo1*/
always @(posedge clk) begin
  if(op_tlbr)begin
    c0_pfn1<=cw_pfn1;
		c0_c1<=cw_c1;
		c0_d1<=cw_d1;
		c0_v1<=cw_v1;
		c0_g1<=cw_g1;
  end
	else if(mct0_we && c0_addr==`CR_ENTRILO1)begin
		c0_pfn1<=c0_wdata[25:6];
		c0_c1<=c0_wdata[5:3];
		c0_d1<=c0_wdata[2];
		c0_v1<=c0_wdata[1];
		c0_g1<=c0_wdata[0];
	end
end
/*index*/
reg c0_p;
always @(posedge clk) begin
	if(reset)begin
		c0_p<=0;
	end
  if(op_tlbp)begin
    c0_index<=cw_index;
    c0_p<=~found;
  end
	else if(mct0_we && c0_addr==`CR_INDEX)begin
		c0_index<=c0_wdata[3:0];
	end
end

/* read data */
always	@(*) begin
	if(c0_addr==`CR_STATUS) begin
		c0_rdata = {9'b0,
							c0_status_bev,
							6'b0,
							c0_status_im,
							6'b0,
							c0_status_exl,
							c0_status_ie
		};
	end
	else if(c0_addr==`CR_CAUSE)begin
		c0_rdata ={c0_cause_bd,
							c0_cause_ti,
							14'b0,
							c0_cause_ip,
							1'b0,
							c0_cause_excode,
							2'b0
		};                               
	end
	else if(c0_addr==`CR_EPC || eret_flush)begin
		c0_rdata  = c0_epc;
	end
	else if(c0_addr==`CR_BADVADDR) begin
		c0_rdata = c0_badvaddr;
	end
	else if(c0_addr==`CR_COUNT) begin
		c0_rdata = c0_count;
	end
	else if(c0_addr==`CR_COMPARE) begin
		c0_rdata = c0_compare;
	end
	else if(c0_addr==`CR_ENTRIHI) begin
		c0_rdata = {c0_vpn2,5'b0,c0_asid};
	end
	else if(c0_addr==`CR_ENTRILO0) begin
		c0_rdata = {2'b0,c0_pfn0,c0_c0,c0_d0,c0_v0,c0_g0};
	end
	else if(c0_addr==`CR_ENTRILO1) begin
		c0_rdata = {2'b0,c0_pfn1,c0_c1,c0_d1,c0_v1,c0_g1};
	end
	else if(c0_addr==`CR_INDEX) begin
		c0_rdata = {c0_p,27'b0,c0_index};
	end

end
//INTERRUPT
    wire hw0,hw1,hw2,hw3,hw4,hw5;
    wire sw0,sw1;
  //  wire interrupt;
    assign hw5 = c0_cause_ip[7] & c0_status_im[7];
    assign hw4 = c0_cause_ip[6] & c0_status_im[6];
    assign hw3 = c0_cause_ip[5] & c0_status_im[5];
    assign hw2 = c0_cause_ip[4] & c0_status_im[4];
    assign hw1 = c0_cause_ip[3] & c0_status_im[3];
    assign hw0 = c0_cause_ip[2] & c0_status_im[2];
    assign sw1 = c0_cause_ip[1] & c0_status_im[1];
    assign sw0 = c0_cause_ip[0] & c0_status_im[0];
    
    assign interrupt = (hw0|hw1|hw2|hw3|hw4|hw5|sw0|sw1)&& (c0_status_ie == 1'b1) && (c0_status_exl == 1'b0);

endmodule